# coding: utf-8

import datetime
import json
import random

from sqlalchemy.sql import or_

from flask_login import login_required, current_user
from flask import (
    request, jsonify, current_app, render_template, flash, Response
)

from app.op.constant import (
    DemandClientSource, SEMGroup, OperateResult, DemandStatus
)
from common.constant import BUSY_7MOOR_AGENTS
from common.seven_moor.constant import SEVEN_MOOR_SERVICE_NO_LIST
from . import sever_moor_bp
from .constant import (
    AnswerState, CallState, CallType, Message, SEVEN_MOOR_RECORD_KEY
)
from .forms import SearchSevenMoorRecordForm
from .models import SevenMoorRecord
from app import db, encrypt
from app.op.models import (
    User, DemandClient, BidUserQueue, Demand
)
from app.axb.models import VirtualNumber, NumberPoolForLT
from app.axb.utils import get_customer_real_mobile
from app.ccws.models import (
    OutBoundRecord, create_outbound_mission, cancel_outbound_mission
)
from app.ccws.constant import CallReason, QueueState


@sever_moor_bp.route('/record_list', methods=['GET', 'POST'])
@login_required
def record_list():
    """
    add by zhouhl 2018-01-15
    通话记录列表
    1. 如果是客服，则只显示当天的记录，且每隔5秒自动刷新记录
    2. 如果不是客服，则显示所有历史记录
    """
    if current_user.seven_moor_account:
        records = SevenMoorRecord.get_today_records()
        return render_template(
            'record_list_for_agent.html',
            records=records,
        )
    else:
        form = SearchSevenMoorRecordForm(formdata=request.form or request.args)
        url_kwargs = {}
        records = SevenMoorRecord.query.filter_by(
            is_bid_queue=False
        )
        if form.customer_no.data:
            customer_no = form.customer_no.data

            is_virtual_number = NumberPoolForLT.get_by_args(tel=customer_no)
            if is_virtual_number:
                # 使用虚拟号查询通话记录的话，会有彩蛋哦~~
                dice_point = random.randint(1, 6)
                if dice_point < 4:
                    flash('这是个虚拟号！你确定不是在调戏系统么?')
                elif dice_point < 6:
                    flash('这是个虚拟号！调戏系统次数太多，系统已经自我觉醒。人工智能初始化中...')
                else:
                    flash('这个是虚拟号！即将派出 terminator')
                return render_template(
                    'record_list.html',
                    form=form,
                )

            records = records.filter(
                or_(
                    SevenMoorRecord.call_no == customer_no,
                    SevenMoorRecord.called_no == customer_no,
                    SevenMoorRecord.customer_real_number == customer_no
                )
            )
        if form.validate():
            query_kwargs, url_kwargs = form.generate_kwargs(
                ignore_keys=('submit', 'customer_no'),
                ignore_values=(None, 'None', '', False)
            )
            if (form.transform_result.data is not None
                and form.transform_result.data != -1):
                query_kwargs['transform_result'] = form.transform_result.data

            records = SevenMoorRecord.generate_list_query_condition(
                args=query_kwargs,
                query=records
            )

        page = request.args.get('page', 1, type=int)
        pagination = records.order_by(SevenMoorRecord.id.desc()).paginate(
            page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'],
            error_out=False)
        records = pagination.items
        return render_template(
            'record_list.html',
            form=form,
            records=records,
            pagination=pagination,
            url_kwargs=url_kwargs,
        )


@sever_moor_bp.route('/ajax/get_record_list')
@login_required
def ajax_get_record_list():
    """
    add by zhouhl 2018-01-15
    ajax接口取当天的最近50条通话记录，供客服异步刷新记录使用
    """
    result = []
    records = list(SevenMoorRecord.get_today_records())[:50]
    for i in records:
        tmp_r = {
            'customer_number': encrypt(i.customer_number),
            'full_customer_number': i.customer_number,
            'customer_nickname': i.customer_nickname,
            'call_type': i.call_type_display,
            'answer_state': i.answer_state_display,
            'begin_datetime': i.created_datetime.strftime('%F %T'),
            'handle_time': i.handle_time_display,
            'agent_name': i.agent_name,
            'transform_result': i.transform_result_display,
            'user_id': i.customer_id,
            'demand_id': i.demand_id,
            'extra_info': i.extra_info
        }
        result.append(tmp_r)

    return jsonify(result)


@sever_moor_bp.route('/webcall_callback')
def webcall_callback():
    """
    add by zhouhl 2018-01-10
    七陌webcall异步调用回调接口

    接口示例：http://www.baidu.com/callback？actionid=Webcall1382293010856915711&Message=4
    """
    action_id = request.args.get("actionid", None)
    message = request.args.get("Message", None)

    print('*' * 50)
    print('action_id = ', action_id)
    print('message = ', message)
    print('*' * 50)

    # 用户拒绝等情况, 把外呼任务置为完成
    if not message or int(message) != Message.CONNECTED.value:
        outbound = OutBoundRecord.get_by_args(
            webcall_action_id=action_id
        )
        if outbound:
            # NOTE 双向外呼没有 outbound 记录，所以不用担心会重复拨打
            outbound.queue_state = QueueState.DONE.value
            # 如果虚拟号外呼拒接，应同时解绑axn虚拟号
            if outbound.virtual_number_id:
                VirtualNumber.unbind_virtual_by_id(outbound.virtual_number_id)
            if outbound.agent_id:
                current_app.redis.srem(BUSY_7MOOR_AGENTS, outbound.agent_id)

            # 用户拒接后续处理
            handle_reject_call(
                user_id=outbound.user_id,
                user_mobile=outbound.mobile,
                outbound=outbound,
                call_reason=outbound.call_reason,
            )

    ##########################################
    # NOTE by zhouhl 2018-03-12 这里是否防止 call_event 比 webcall_callback 早返回，造成数据错误？但是 call_event 里面新建记录时就做了相关处理，这里是否必要？
    # 如果action_id在BidUserQueue表中, 则设置7moor的数据的is_bid_queue=True
    queue = BidUserQueue.get_by_args(action_id=action_id)
    if queue:
        moor_rec = SevenMoorRecord.get_by_args(webcall_action_id=action_id)
        if moor_rec:
            moor_rec.is_bid_queue = True
    ##########################################

    return jsonify()


@sever_moor_bp.route('/call_event')
def call_event():
    """
    add by zhouhl 2018-01-10
    七陌通话状态事件推送接口
    """
    data = init_call_event_data(request.args)

    # 根据CallSheetID查找，有则更新，无则创建
    seven_moor_key = SEVEN_MOOR_RECORD_KEY.format(
        call_sheet_id=data['call_sheet_id']
    )
    record_in_redis = current_app.redis.get(seven_moor_key)
    if record_in_redis:
        exists_record = SevenMoorRecord.get_by_args(
            call_sheet_id=data['call_sheet_id']
        )
        if exists_record:
            exists_record.update(**data)
    else:
        current_app.redis.set(seven_moor_key, '1')
        exists_record = SevenMoorRecord.create(**data)

        # alter by zhouhl 2018-05-08
        # 新建七陌外呼记录时，将OutBoundRecord回写成Done，防止通话超过5分钟时，系统重复拨打
        action_id = data['webcall_action_id'] or data['action_id']
        outbound = OutBoundRecord.get_by_args(
            webcall_action_id=action_id
        )
        if outbound:
            outbound.queue_state = QueueState.CALLING.value

        # 是否双向外呼。非双向外呼时，还需要判断是否使用虚拟号，并查找真实号码
        queue = BidUserQueue.get_by_args(action_id=data['webcall_action_id'])
        if queue:
            exists_record.is_bid_queue = True
        else:
            customer_mobile = get_customer_mobile(data)
            customer_real_number = get_customer_real_mobile(customer_mobile)
            if customer_mobile != customer_real_number:
                exists_record.use_virtual_number = True
                exists_record.customer_real_number = customer_real_number

    call_state = data['call_state']
    if call_state not in [CallState.HANGUP.value, CallState.UNLINK.value]:
        # 通话时的中间状态直接返回，目前不做任何处理
        # 返回给7陌200字符串, 表示接收成功, 否则7陌的重发机制会重发请求
        return Response('200')

    # 通话结束时的处理逻辑
    # STEP1.回写外呼任务OutBoundRecord，仅解绑虚拟号
    current_app.redis.delete(seven_moor_key)
    action_id = data['webcall_action_id'] or data['action_id']
    outbound = OutBoundRecord.get_by_args(
        webcall_action_id=action_id
    )
    if outbound:
        outbound.queue_state = QueueState.DONE.value
        # 解绑外呼的虚拟号，axn关系
        if outbound.virtual_number_id:
            VirtualNumber.unbind_virtual_by_id(outbound.virtual_number_id)
        if outbound.agent_id:
            current_app.redis.srem(BUSY_7MOOR_AGENTS, outbound.agent_id)

    # 取客户的真实号码
    if exists_record.use_virtual_number:
        real_mobile = exists_record.customer_real_number
    else:
        real_mobile = get_customer_mobile(data)

    # alter by zhouhl 2018-05-10
    # 客户自主呼入(排除自动外呼的记录)时，写入demand_client数据 迁移至此，解决重复新建用户的bug
    # FIXED by zhouhl 客户首次主动呼入时，可能会在新建User时失败，因为弹窗时 ajax_get_user_intro 方法里面也有新建User的方法
    if data['call_type'] == CallType.NORMAL.value \
        and data['called_no'] not in SEVEN_MOOR_SERVICE_NO_LIST \
        and data['call_no'] not in SEMGroup.values():

        user = User.get_by_args(
            mobile=real_mobile
        ) or User.create(
            nickname='万屏汇客户',
            mobile=real_mobile
        )
        DemandClient.visit(
            user.id,
            DemandClientSource.CALL_IN.value,
            sem_called=data['called_no']
        )

    print('*' * 50)
    print('call_no =', data['call_no'])
    print('called_no =', data['called_no'])
    print('real_mobile:', real_mobile)
    print('call_type =', data['call_type'])
    print('call_state:', call_state)
    print('outbound:', outbound)
    print('answer_state:', data['answer_state'])
    print('call_start_at:', data['begin'])
    print('*' * 50)

    # STEP2.如果 answer_state != 'dealing'，即客服未正常接听，需考虑再次呼叫
    if data['answer_state'] != AnswerState.DEALING.value:
        # 只有非桥接通话，才回呼客户
        if not exists_record.is_bid_queue:
            user = User.get_or_create(real_mobile)

            # 用户拒接后续处理
            handle_reject_call(
                user_id=user.id,
                user_mobile=real_mobile,
                outbound=outbound,
                call_reason=CallReason.MISS_CALLBACK.value,
            )

    # STEP3.如果 answer_state=='dealing'，
    # 取消其他外呼任务，上传录音，解绑外呼虚拟号
    else:
        # STEP3.1 客户已接, 把此客户的外呼任务全部取消
        cancel_outbound_mission(real_mobile)

        # STEP3.2 上传录音，需要的参数：录音地址、begin、客户真实号码
        record_url = data['file_server'] + '/' + data['record_file']
        record_datetime = data['begin']
        audio_file_name = data['record_file'].split('?')[0].split('/')[-1]
        date_info = datetime.date.today().strftime('%Y%m%d')

        print('$' * 50)
        print(current_app.sever_moor_audio_upload_producer)
        print('$' * 50)
        print({
            'mobile': real_mobile,
            'record_url': record_url,
            'record_datetime': record_datetime,
            'audio_file_name': audio_file_name,
            'call_id': data['call_id'],
        })
        print('$' * 50)

        if current_app.sever_moor_audio_upload_producer:
            current_app.sever_moor_audio_upload_producer.send(
                json.dumps({
                    'mobile': real_mobile,
                    'record_url': record_url,
                    'record_datetime': record_datetime,
                    'audio_file_name': audio_file_name,
                    'call_id': data['call_id'],
                    'date_info': date_info,
                })
            )

    # 返回给7陌200字符串, 表示接收成功, 否则7陌的重发机制会重发请求
    return Response('200')


def get_customer_mobile(data):
    """
    取客户号码逻辑
    1. call_type == 'normal' 时，客户号码为 call_no 主叫号码
    2. call_type == 'dialout' 是，客户号码为 called_no 被叫号码
    3. 其他情况直接返回 call_no 主叫号码
    """
    if data['call_type'] == CallType.NORMAL.value:
        return data['call_no']

    if data['call_type'] == CallType.DIALOUT.value:
        return data['called_no']

    return data['call_no']


def init_call_event_data(request_args):
    """
    add by zhouhl 2018-03-09
    根据 request.args 解析参数
    """
    return {
        'call_no': request_args.get('CallNo'),
        'called_no': request_args.get('CalledNo'),
        'call_sheet_id': request_args.get('CallSheetID'),
        'call_type': request_args.get('CallType'),
        'ring': request_args.get('Ring') or None,
        'begin': request_args.get('Begin') or None,
        'end': request_args.get('End') or None,
        'queue_time': request_args.get('QueueTime') or None,
        'agent': request_args.get('Agent'),
        'agent_name': request_args.get('AgentName'),
        'queue': request_args.get('Queue'),
        'answer_state': request_args.get('State'),
        'call_state': request_args.get('CallState'),
        'action_id': request_args.get('ActionID') or None,
        'webcall_action_id': request_args.get('WebcallActionID') or None,
        'record_file': request_args.get('RecordFile'),
        'file_server': request_args.get('FileServer'),
        'province': request_args.get('Province'),
        'district': request_args.get('District'),
        'call_id': request_args.get('CallID'),
        'ivr_key': request_args.get('IVRKEY')
    }


def handle_reject_call(user_id, user_mobile, outbound, call_reason):
    """
    add by zhouhl 2018-03-13
    非正常通话的后续处理流程
    STEP1 将 outbound 标记为拒接
    STEP2 判断用户累计拒接次数。未达上限则继续外呼；否则更新 demand_client
    """
    # outbound 可能为 None，如首次客户呼入后的非正常通话
    if outbound:
        outbound.webcall_reject = True

    # 查询用户累计拒接次数
    reject_count = OutBoundRecord.query.filter(
        OutBoundRecord.mobile == user_mobile,
        OutBoundRecord.webcall_reject.__eq__(True),
        OutBoundRecord.schedule_time >= datetime.date.today(),
    ).count()
    reject_count_min_map = {
        0: 3,
        1: 3,
        2: 5,
        3: 10,
    }

    # 用户拒绝时回呼
    if reject_count < 4:
        tmp_outbound_record, err = create_outbound_mission(
            user_id=user_id,
            mobile=user_mobile,
            call_reason=call_reason,
            schedule_time=datetime.datetime.now() \
                + datetime.timedelta(
                    minutes=reject_count_min_map.get(reject_count, 3)
                ),
        )
        if tmp_outbound_record:
            db.session.add(tmp_outbound_record)
    # 拒接次数达到上限(4)时，同步修改 DemandClient 的转化结果为 回呼拒绝达到上限
    else:
        demand_client = DemandClient.query.filter(
            DemandClient.user_id==user_id,
            DemandClient.operate_result.in_([
                OperateResult.NA.value,
                OperateResult.DELAY_OP.value,
            ])
        ).first()
        if demand_client:
            demand_client.operate_result = OperateResult.REJECT_MAX.value \
                if demand_client.operate_result == OperateResult.NA.value \
                else OperateResult.DELAY_REJECT_MAX.value
            # 拒接达到上限，也绑定最近的一个草稿单，供SEM优化
            draft_demand = Demand.get_last_draft_demand(user_id)
            if draft_demand:
                demand_client.demand_id = draft_demand.id
                draft_demand.status = DemandStatus.INVALID.value

